#include "preHeader.h"
#include "Session/PlayerPacketHandler.h"
#include "Object/Player.h"

GErrorCode Player::HandleBuyShopItem(uint32 spId, uint32 num)
{
	if (num == 0 || num > BUY_SHOP_ITEM_COUNT) {
		return InvalidRequest;
	}
	auto pProto = GetDBEntry<ShopPrototype>(spId);
	if (pProto == NULL) {
		return InvalidRequest;
	}

	ShopItemStatus* pStatus = NULL;
	uint32 maxBuyNum = pProto->bundleNumMax != 0 ?
		std::min(pProto->bundleNumMax, num) : num;
	bool isBuyLimit = IsShopPrototypeBuyLimit(pProto);
	if (isBuyLimit) {
		auto itr = m_shopStatus.find(spId);
		if (itr != m_shopStatus.end()) {
			pStatus = &itr->second;
		}
		maxBuyNum = GetBuyShopItemMaxCount4BuyLimit(
			pProto, pStatus != NULL ? *pStatus : defaultShopItemStatus);
		if (maxBuyNum == 0) {
			return ErrShopItemSellOut;
		}
	}

	auto errCode = TryBuyShopItem(true, maxBuyNum, *pProto);
	if (errCode != CommonSuccess) {
		return errCode;
	}

	if (isBuyLimit) {
		if (pStatus == NULL) {
			pStatus = &m_shopStatus[spId];
		}
		ApplyBuyShopItem4BuyLimit(pProto, *pStatus, maxBuyNum);
	}

	return CommonSuccess;
}

GErrorCode Player::HandleBuySpecialShopItem(INetPacket& pck,
	bool isBuy, uint32 maxBuyNum, const SpecialShopPrototype& ssProto)
{
	ShopItemStatus* pStatus = NULL;
	bool isBuyLimit = IsSpecialShopPrototypeCharBuyLimit(&ssProto);
	if (isBuyLimit) {
		auto itr = m_specialShopStatus.find(ssProto.itemUniqueKey);
		if (itr != m_specialShopStatus.end()) {
			pStatus = &itr->second;
		}
		maxBuyNum = GetBuySpecialShopItemMaxCount4CharBuyLimit(
			&ssProto, pStatus != NULL ? *pStatus : defaultShopItemStatus);
		if (maxBuyNum == 0) {
			return ErrShopItemSellOut;
		}
	}

	auto errCode = TryBuyShopItem(isBuy, maxBuyNum, ssProto);
	if (errCode != CommonSuccess) {
		return errCode;
	}

	if (isBuy && isBuyLimit) {
		if (pStatus == NULL) {
			pStatus = &m_specialShopStatus[ssProto.itemUniqueKey];
		}
		ApplyBuySpecialShopItem4CharBuyLimit(&ssProto, *pStatus, maxBuyNum);
		pStatus->expireTime = ssProto.itemEndTime;
	}

	pck << maxBuyNum;
	return CommonSuccess;
}

GErrorCode Player::TryBuyShopItem(
	bool isBuy, uint32 maxBuyNum, const ShopPrototype& spProto)
{
	auto pItemProto = GetDBEntry<ItemPrototype>(spProto.itemTypeID);
	if (pItemProto == NULL) {
		return CommonInternalError;
	}

	auto itemProp = NewItemProp4Flags(spProto.itemTypeID,
		u32(spProto.itemCount * maxBuyNum), spProto.itemFlags);
	uint32 lackSlotCnt = m_pItemStorage->
		GetFillItemLackSlotCount(pItemProto, itemProp);
	if (lackSlotCnt > 0) {
		return ErrItemStorageSlotNotEnough;
	}

	auto buyPrice = spProto.sellPrice * maxBuyNum;
	if (spProto.currencyType < ITEM_CURRENCY_TYPE) {
		if (!IsMoneyEnough(CurrencyType(spProto.currencyType), buyPrice)) {
			return ErrMoneyNotEnough;
		}
		if (isBuy) {
			CostMoney(CurrencyType(
				spProto.currencyType), buyPrice, CFT_SHOP, {});
		}
	} else {
		if (!m_pItemStorage->IsItemEnough(spProto.currencyType, buyPrice)) {
			return ErrItemCountNotEnough;
		}
		if (isBuy) {
			m_pItemStorage->RemoveItemAmount(
				spProto.currencyType, buyPrice, IFT_SHOP, {});
		}
	}

	if (isBuy) {
		m_pItemStorage->CreateAddItem(pItemProto, itemProp, IFT_SHOP, {}, true);
	}

	return CommonSuccess;
}

void Player::PackShopItemList(INetPacket& pck, uint32 shopType,
	const std::unordered_map<uint64, SpecialShopPrototype>& ssProtos)
{
	uint16 n = 0;
	size_t anchor = pck.Placeholder<u16>(0);
	for (auto&[itemUniqueKey, ssStatus] : m_specialShopStatus) {
		auto itr = ssProtos.find(itemUniqueKey);
		if (itr != ssProtos.end()) {
			auto pProto = &itr->second;
			if (shopType == 0 || shopType == pProto->shopType) {
				pck << itemUniqueKey << ssStatus.dailyCount
					<< ssStatus.weeklyCount << ssStatus.totalCount;
				n += 1;
			}
		}
	}
	pck.Put(anchor, n);

	n = 0;
	anchor = pck.Placeholder<u16>(0);
	auto pSPTable = sDBMgr.GetTable<ShopPrototype>();
	for (auto&[spId, ssStatus] : m_shopStatus) {
		auto pProto = pSPTable->GetEntry(spId);
		if (pProto != NULL) {
			if (shopType == 0 || shopType == pProto->shopType) {
				pck << spId << ssStatus.dailyCount
					<< ssStatus.weeklyCount;
				n += 1;
			}
		}
	}
	pck.Put(anchor, n);
}

int PlayerPacketHandler::HandleGetShopItemList(Player *pPlayer, INetPacket &pck)
{
	size_t anchor = pck.GetReadPos();
	uint32 shopType;
	pck >> shopType;

	std::unordered_map<uint64, SpecialShopPrototype> specialShopPrototypes;
	uint16 n = pck.Read<uint16>();
	specialShopPrototypes.reserve(n);
	for (uint16 i = 0; i < n; ++i) {
		LoadFromINetStream(specialShopPrototypes[pck.Read<uint32>()], pck);
	}

	pck.AdjustReadPos(anchor - pck.GetReadPos());
	pPlayer->PackShopItemList(pck, shopType, specialShopPrototypes);
	pPlayer->SendPacket(pck);

	return SessionHandleSuccess;
}

int PlayerPacketHandler::HandleBuyShopItem(Player *pPlayer, INetPacket &pck)
{
	uint32 spId, num;
	pck >> spId >> num;
	GErrorCode errCode = pPlayer->HandleBuyShopItem(spId, num);
	if (errCode != CommonSuccess) {
		pPlayer->SendError(errCode);
	}
	return SessionHandleSuccess;
}

int PlayerPacketHandler::HandleBuySpecialShopItem(Player *pPlayer, INetPacket &pck, const RPCActor::RequestMetaInfo &info)
{
	bool isBuy;
	uint32 maxBuyNum;
	SpecialShopPrototype ssProto;
	pck >> isBuy >> maxBuyNum;
	LoadFromINetStream(ssProto, pck);
	NetPacket rpcRespPck(MS_RPC_INVOKE_RESP);
	size_t anchor = rpcRespPck.Placeholder((int32)CommonSuccess);
	auto errCode = pPlayer->HandleBuySpecialShopItem(rpcRespPck, isBuy, maxBuyNum, ssProto);
	if (errCode != CommonSuccess) {
		rpcRespPck.Put(anchor, (int32)errCode);
	}
	pPlayer->RPCReply2Gs(rpcRespPck, info.sn);
	return SessionHandleSuccess;
}
